package cn.tk.engine.mr;

import cn.tk.cache.mr.EntityBuf;
import cn.tk.cache.mr.FlowBuf;
import cn.tk.model.entity.mr.EntityValue;
import cn.tk.model.entity.mr.Flow;
import cn.tk.model.entity.mr.FlowNode;
import cn.tk.model.pojo.mr.engine.ClarifyItem;
import cn.tk.model.pojo.mr.engine.ClarifyResult;
import cn.tk.model.pojo.mr.flow.EntityClarify;
import cn.tk.model.pojo.mr.flow.EntityNodeDTO;
import cn.tk.model.pojo.mr.nlu.NluResp;
import cn.tk.service.mr.EntityNodeService;
import cn.tk.service.mr.EntityService;
import cn.tk.service.mr.SessionService;
import cn.tk.utils.EmptyUtil;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by denglw on 2021/8/6.<br/>
 * Desc: 实体节点处理逻辑
 */
@Slf4j
@Component
public class EntityNodeDeal {

    @Autowired
    private EntityNodeService entityNodeService;

    @Autowired
    private EntityService entityService;

    @Autowired
    private SessionService sessionService;

    /**
     * 匹配实体子节点.<br/>
     * @param flowId 流程id
     * @param lastNodeId 上一轮次末尾节点id
     * @return 实体节点
     */
    public EntityNodeDTO searchChild(Long flowId, Long lastNodeId) {
        if (EmptyUtil.anyNull(flowId, lastNodeId)) {
            return null;
        }
        FlowBuf flowBuf = FlowBuf.getInstance();
        Flow flow = flowBuf.getFlow(flowId);
        if (flow == null) {
            return null;
        }
        // 1、匹配子节点中是否有实体节点：存在即直接跳转
        List<Long> childNodeIds = flowBuf.childEntityNodeIds(lastNodeId);
        List<EntityNodeDTO> childNodes = entityNodeService.entityNodes(childNodeIds);
        for (EntityNodeDTO childNode : childNodes) {
            FlowNode flowNode = flowBuf.getFlowNode(childNode.getFlowId(), childNode.getNodeId());
            if (flowNode != null && flowNode.isEntityNode()) {
                return childNode;
            }
        }
        return null;
    }

    /**
     * 匹配可跳转实体节点.<br/>
     * @param nlu 模型结果
     * @param flowId 流程id
     * @return 实体节点
     */
    public EntityNodeDTO searchRecover(NluResp nlu, Long flowId) {
        if (EmptyUtil.isNull(flowId)) {
            return null;
        }
        FlowBuf flowBuf = FlowBuf.getInstance();
        Flow flow = flowBuf.getFlow(flowId);
        if (flow == null) {
            return null;
        }
        // 匹配可跳转的实体节点：本流程的优先
        List<EntityNodeDTO> recoverableNodes = entityNodeService.recoverableEntityNodes(flow.getBotId());
        recoverableNodes.sort((a, b) -> {
            if (Objects.equals(a.getFlowId(), flowId)) {
                return 1;
            }
            if (Objects.equals(b.getFlowId(), flowId)) {
                return -1;
            }
            return 0;
        });
        return this.searchEntityNode(nlu, recoverableNodes);
    }

    /**
     * 进行实体节点的实体澄清.<br/>
     * @param nlu 模型结果
     * @param node 待澄清的实体节点
     * @param clarifyHis 该实体节点历史的澄清记录
     * @return 澄清结果
     */
    public ClarifyResult clarify(NluResp nlu, EntityNodeDTO node, List<ClarifyItem> clarifyHis) {
        ClarifyResult result = new ClarifyResult();
        if (EmptyUtil.anyNull(nlu, node)) {
            return result;
        }
        Long nodeId = node.getNodeId();
        Boolean appendable = node.getAppendable();
        List<EntityClarify> entityClarifies = node.getEntityClarifies();
        // 封装返回结果
        result.setNodeId(nodeId);
        // 澄清列表: 使用历史澄清结果进行初始化
        List<ClarifyItem> clarifyItems = this.initClarifyItems(entityClarifies, clarifyHis);
        // 依据nlu的结果，进行匹配及未匹配澄清逻辑
        this.clarifyMatch(clarifyItems, nlu, appendable);
        result.setClarifyItems(clarifyItems);

        // 检视匹配的情况，依据此决定是否选择实体进行澄清
        for (ClarifyItem i : clarifyItems) {
            boolean clarified = i.isClarified();
            if (clarified) {
                continue;
            }
            // 如果没有匹配，需要进行澄清
            Optional<EntityClarify> optionalEntityClarify = entityClarifies.stream()
                    .filter(a -> Objects.equals(a.getEntityId(), i.getEntityId())).findFirst();
            if (!optionalEntityClarify.isPresent()) {
                continue;
            }
            EntityClarify entityClarify = optionalEntityClarify.get();
            int clarifyNum = i.getClarifyNum();
            List<EntityClarify.ClarifyReply> replies = entityClarify.getClarifyReplies();
            // 1、如果达到澄清上限，整个节点返回失败
            if (clarifyNum > entityClarify.getClarifyNum()) {
                result.setClarifyStatus(3);
                result.setClarifyReplyId(entityClarify.getBottomReplyId());
                return result;
            }

            // 2、如果是必须澄清节点且次数未达上限；或者；是非必须澄清节点但还未进行一轮澄清，则进行常规澄清操作
            Boolean necessary = entityClarify.getNecessary();
            if (Objects.equals(necessary, true) || (Objects.equals(necessary, false) && clarifyNum == 0)) {
                if (!EmptyUtil.emptyColl(replies)) {
                    result.setClarifyEntityId(i.getEntityId());
                    result.setClarifyStatus(2);
                    EntityClarify.ClarifyReply reply = replies.get(new Random().nextInt(replies.size()));
                    result.setClarifyReplyId(reply.getReplyId());
                    // 使用指针方式，更改具体实体的澄清情况，随结果一并返回
                    i.setClarifyNum(clarifyNum + 1);
                    return result;
                }
            }

            // 3、如果是非必须澄清节点：如果一轮没有匹配到结果，则使用默认值
            if (Objects.equals(necessary, false) && clarifyNum > 0) {
                i.setClarified(true);
                // 使用默认值
                Long defaultEntityValueId = entityClarify.getDefaultEntityValueId();
                EntityValue defaultValue = entityService.getValue(defaultEntityValueId);
                String content = defaultValue == null ? "" : defaultValue.getContent();
                i.setClarifyValues(Lists.newArrayList(new ClarifyItem.ClarifyValue(defaultEntityValueId, content)));
            }
        }
        result.setClarifyStatus(1);
        return result;
    }

    /**
     * 根据nlu结果进行实体值匹配<br/>
     * @param clarifyItems 待澄清项
     * @param nlu nlu
     * @param appendable 是否可追加
     */
    private void clarifyMatch(List<ClarifyItem> clarifyItems, NluResp nlu, Boolean appendable) {
        List<NluResp.InnerEntity> entities = this.packNluEntities(nlu);
        for (ClarifyItem item : clarifyItems) {
            String entityCode = item.getEntityCode();
            List<ClarifyItem.ClarifyValue> clarifyValues = item.getClarifyValues();
            List<EntityValue> entityValues = entityService.getValues(item.getEntityId());
            for (NluResp.InnerEntity innerEntity : entities) {
                boolean match = Objects.equals(innerEntity.getEntityCode(), entityCode);
                if (!match) {
                    continue;
                }
                // 如果匹配，则进行赋值
                String value = innerEntity.getValue();
                EntityValue entityValue = entityValues.stream().filter(i -> Objects.equals(i.getContent(), value)).findFirst().orElse(null);
                if (entityValue == null) {
                    continue;
                }
                // 匹配成功: 如果是追加模式，进行追加；否则进行覆盖
                item.setClarified(true);
                Long valueId = entityValue.getId();
                String content = entityValue.getContent();
                if (Objects.equals(appendable, true)) {
                    boolean anyMatch = clarifyValues.stream().anyMatch(i -> Objects.equals(i.getValueId(), valueId));
                    if (!anyMatch) {
                        clarifyValues.add(new ClarifyItem.ClarifyValue(valueId, content));
                    }
                } else {
                    clarifyValues.clear();
                    clarifyValues.add(new ClarifyItem.ClarifyValue(valueId, content));
                }
            }
        }
    }

    /**
     * 初始化待澄清项列表.<br/>
     * @param entityClarifies 实体节点的澄清列表
     * @param clarifyHis 澄清历史
     * @return list
     */
    private List<ClarifyItem> initClarifyItems(List<EntityClarify> entityClarifies, List<ClarifyItem> clarifyHis) {
        List<ClarifyItem> clarifyItems = new ArrayList<>(clarifyHis);
        EntityBuf entityBuf = EntityBuf.getInstance();
        for (EntityClarify clarify : entityClarifies) {
            Long entityId = clarify.getEntityId();
            boolean contains = clarifyItems.stream().anyMatch(i -> Objects.equals(i.getEntityId(), entityId));
            if (contains) {
                continue;
            }
            // 使用实体节点中的实体进行初始化
            ClarifyItem item = new ClarifyItem();
            item.setClarified(false);
            item.setClarifyNum(0);
            item.setClarifyValues(new ArrayList<>());
            item.setEntityCode(entityBuf.getCode(entityId));
            item.setEntityId(entityId);
            clarifyItems.add(item);
        }
        return clarifyItems;
    }

    /**
     * 封装模型的实体响应，包括历史的.<br/>
     * @param nlu 模型结果
     * @return list
     */
    private List<NluResp.InnerEntity> packNluEntities(NluResp nlu) {
        List<NluResp.InnerEntity> entities = new ArrayList<>(EmptyUtil.nullToEmptyList(nlu.getEntities()));
        List<NluResp.InnerEntity> hisNluEntities = sessionService.getNluEntities();
        // 去重
        for (NluResp.InnerEntity his : hisNluEntities) {
            boolean anyMatch = entities.stream().anyMatch(i ->
                    Objects.equals(i.getEntityCode(), his.getEntityCode()) && Objects.equals(i.getValue(), his.getValue()));
            if (anyMatch) {
                continue;
            }
            entities.add(his);
        }
        return entities;
    }

    private EntityNodeDTO searchEntityNode(NluResp nlu, List<EntityNodeDTO> nodes) {
        EntityBuf entityBuf = EntityBuf.getInstance();
        List<NluResp.InnerEntity> entities = nlu.getEntities();
        if (EmptyUtil.emptyColl(entities)) {
            return null;
        }
        List<String> entityCodes = entities.stream().filter(i -> !EmptyUtil.blank(i.getValue()))
                .map(NluResp.InnerEntity::getEntityCode).collect(Collectors.toList());
        for (EntityNodeDTO node : nodes) {
            List<EntityClarify> entityClarifies = node.getEntityClarifies();
            if (EmptyUtil.emptyColl(entityClarifies)) {
                continue;
            }
            // 待澄清的实体列表：与模型识别的实体列表进行匹配，如果有交集，则进入
            for (EntityClarify clarify : entityClarifies) {
                String code = entityBuf.getCode(clarify.getEntityId());
                if (entityCodes.contains(code)) {
                    return node;
                }
            }
        }
        return null;
    }
}
